import config from "./config.js";
import { camelize, extend, hasOwn, isPlainObject } from "../util/index.js";

function defaultStrat(parentVal, childVal) {
  return childVal === undefined ? parentVal : childVal;
}

const strats = config.optionMergeStrategies;

/**
 * 合并选项
 * @param {*} parent 父类的选项
 * @param {*} child 子类的选项
 * @param {*} vm 当前vm实例
 */
export function mergeOptions(parent, child, vm) {
  // 如果child是一个Vue构造函数
  if (typeof child === "function") {
    child = child.options;
  }
  // 重新设置props
  normalizeProps(child);
  // 重新设置inject
  normalizeInject(child);
  // 重新设置directives
  normalizeDirectives(child);

  if (!child._base) {
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm);
    }
    if (child.mixins) {
      for (let mixin of child.mixins) {
        parent = mergeOptions(parent, mixin, vm);
      }
    }
  }

  const options = {};
  let key;
  for (key in parent) {
    mergeField(key);
  }

  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key);
    }
  }

  function mergeField(key) {
    // 使用对应的合并策略
    const strat = strats[key] || defaultStrat;
    // 合并对应的选项
    options[key] = strat(parent[key], child[key], vm);
  }
  return options;
}

/**
 * 通用化props选项
 * @param {*} options 选项
 */
function normalizeProps(options) {
  const props = options.props;
  // 没有props设置
  if (!props) return;
  const res = {};
  let name, val;
  if (Array.isArray(props)) {
    // props为一个数组
    for (const prop of props) {
      if (typeof prop === "string") {
        name = camelize(prop);
        res[name] = { type: null };
      }
    }
  } else if (isPlainObject(obj)) {
    for (const key in props) {
      val = props[key];
      name = camelize(key);
      res[name] = isPlainObject(val) ? val : { type: val };
    }
  }
  options.props = res;
}

function normalizeInject(options) {
  const inject = options.inject;
  if (!inject) return;
  const normalized = (options.inject = {});
  if (Array.isArray(inject)) {
    for (const val of inject) {
      normalized[val] = { from: val };
    }
  } else if (isPlainObject(inject)) {
    let val;
    for (const key in inject) {
      val = inject[key];
      normalized[key] = isPlainObject(val)
        ? extend({ from: key }, val)
        : { from: val };
    }
  }
}

function normalizeDirectives(options) {
  const dirs = options.directives;
  if (dirs) {
    for (const key in dirs) {
      const dir = dirs[key];
      if (typeof dir === "function") {
        dirs[key] = { bind: dir, update: dir };
      }
    }
  }
}
